const fs = require('fs');
const path = require('path');
const {
    minify
} = require('terser');
const htmlMinifier = require('html-minifier-terser');
const CleanCSS = require('clean-css');
const babel = require('@babel/core');

// 尝试加载 Vue 2 和 Vue 3 编译器
let vue2Compiler;
let vue3Compiler;

try {
    vue2Compiler = require('vue-template-compiler');
} catch (e) {
    // 忽略错误，Vue 2 编译器可能未安装
}

try {
    vue3Compiler = require('@vue/compiler-sfc');
} catch (e) {
    // 忽略错误，Vue 3 编译器可能未安装
}

// 输入和输出目录
const INPUT_DIR = path.join(__dirname, 'src/input');
const OUTPUT_DIR = path.join(__dirname, 'src/output');

// Babel 配置
const babelConfig = {
    presets: [
        '@babel/preset-env',
        '@babel/preset-react'
    ],
    plugins: [
        ['minify-mangle-names', {
            exclude: ['React', 'Component']
        }]
    ]
};

// 文件处理函数映射
const fileProcessors = {
    '.js': processJS,
    '.jsx': processJSX,
    '.tsx': processTSX,
    '.vue': processVue,
    '.html': processHTML,
    '.htm': processHTML,
    '.css': processCSS
};

async function processJS(code) {
    const options = {
        compress: {
            dead_code: true,
            drop_console: true,
            booleans: true,
        },
        mangle: {
            toplevel: true,
            properties: false,
        },
        output: {
            beautify: false,
            comments: false,
        }
    };

    const result = await minify(code, options);
    if (result.error) throw new Error(`JS 混淆错误: ${result.error}`);
    return result.code;
}

async function processJSX(code) {
    try {
        const {
            code: transformedCode
        } = await babel.transformAsync(code, babelConfig);
        return processJS(transformedCode);
    } catch (error) {
        throw new Error(`JSX 处理错误: ${error.message}`);
    }
}

async function processTSX(code) {
    try {
        const {
            code: transformedCode
        } = await babel.transformAsync(code, {
            ...babelConfig,
            presets: [...babelConfig.presets, '@babel/preset-typescript']
        });
        return processJS(transformedCode);
    } catch (error) {
        throw new Error(`TSX 处理错误: ${error.message}`);
    }
}

async function processHTML(code) {
    const options = {
        collapseWhitespace: true,
        removeComments: true,
        removeRedundantAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true,
        useShortDoctype: true,
        minifyCSS: true,
        minifyJS: true
    };

    return htmlMinifier.minify(code, options);
}

async function processCSS(code) {
    const result = new CleanCSS({}).minify(code);
    if (result.errors.length > 0) throw new Error(`CSS 压缩错误: ${result.errors[0]}`);
    return result.styles;
}

async function processVue(code) {
    try {
        // 检测 Vue 文件类型
        const isVue3 = code.includes('<script setup>') || !vue2Compiler;

        if (isVue3 && vue3Compiler) {
            // 处理 Vue 3 文件
            const {
                descriptor
            } = vue3Compiler.parse(code);
            let result = code;

            // 处理 <script> 和 <script setup>
            if (descriptor.script || descriptor.scriptSetup) {
                const scriptContent = (descriptor.script && descriptor.script.content) ||
                    (descriptor.scriptSetup && descriptor.scriptSetup.content) ||
                    '';
                const obfuscatedScript = await processJS(scriptContent);

                if (descriptor.script) {
                    result = result.replace(
                        `<script>${descriptor.script.content}</script>`,
                        `<script>${obfuscatedScript}</script>`
                    );
                }

                if (descriptor.scriptSetup) {
                    result = result.replace(
                        `<script setup>${descriptor.scriptSetup.content}</script>`,
                        `<script setup>${obfuscatedScript}</script>`
                    );
                }
            }

            // 处理 <style>
            for (const style of descriptor.styles) {
                if (style.content) {
                    const minifiedStyle = await processCSS(style.content);
                    result = result.replace(
                        `<style${style.scoped ? ' scoped' : ''}>${style.content}</style>`,
                        `<style${style.scoped ? ' scoped' : ''}>${minifiedStyle}</style>`
                    );
                }
            }

            // 处理 <template>
            if (descriptor.template && descriptor.template.content) {
                const minifiedTemplate = await processHTML(descriptor.template.content);
                result = result.replace(
                    `<template>${descriptor.template.content}</template>`,
                    `<template>${minifiedTemplate}</template>`
                );
            }

            return result;

        } else if (vue2Compiler) {
            // 处理 Vue 2 文件
            const parts = vue2Compiler.parseComponent(code);
            let result = code;

            // 处理 <script>
            if (parts.script && parts.script.content) {
                const obfuscatedScript = await processJS(parts.script.content);
                result = result.replace(
                    `<script>${parts.script.content}</script>`,
                    `<script>${obfuscatedScript}</script>`
                );
            }

            // 处理 <style>
            if (parts.styles && parts.styles.length) {
                for (const style of parts.styles) {
                    if (style.content) {
                        const minifiedStyle = await processCSS(style.content);
                        result = result.replace(
                            `<style${style.scoped ? ' scoped' : ''}>${style.content}</style>`,
                            `<style${style.scoped ? ' scoped' : ''}>${minifiedStyle}</style>`
                        );
                    }
                }
            }

            // 处理 <template>
            if (parts.template && parts.template.content) {
                const minifiedTemplate = await processHTML(parts.template.content);
                result = result.replace(
                    `<template>${parts.template.content}</template>`,
                    `<template>${minifiedTemplate}</template>`
                );
            }

            return result;

        } else {
            // 没有可用的编译器
            console.warn('警告: 找不到 Vue 编译器，Vue 文件将保持不变');
            return code;
        }
    } catch (error) {
        throw new Error(`Vue 文件处理错误: ${error.message}`);
    }
}

async function processFile(inputPath, outputPath) {
    try {
        const ext = path.extname(inputPath).toLowerCase();
        const processor = fileProcessors[ext];

        if (!processor) {
            // 对于不支持的文件类型，直接复制
            await fs.promises.copyFile(inputPath, outputPath);
            return {
                processed: false
            };
        }

        // 读取文件内容
        const code = await fs.promises.readFile(inputPath, 'utf8');

        // 处理文件
        const processedCode = await processor(code);

        // 确保输出目录存在
        await fs.promises.mkdir(path.dirname(outputPath), {
            recursive: true
        });

        // 写入处理后的文件
        await fs.promises.writeFile(outputPath, processedCode);

        return {
            processed: true,
            originalSize: Buffer.byteLength(code, 'utf8'),
            processedSize: Buffer.byteLength(processedCode, 'utf8')
        };
    } catch (error) {
        console.error(`✖ 处理 ${path.relative(__dirname, inputPath)} 时出错:`, error.message);
        return null;
    }
}

async function processDirectory(dir) {
    const entries = await fs.promises.readdir(dir, {
        withFileTypes: true
    });
    let stats = {
        totalFiles: 0,
        processedFiles: 0,
        originalSize: 0,
        processedSize: 0
    };

    for (const entry of entries) {
        const entryPath = path.join(dir, entry.name);
        const relativePath = path.relative(INPUT_DIR, entryPath);
        const outputPath = path.join(OUTPUT_DIR, relativePath);

        if (entry.isDirectory()) {
            // 递归处理子目录
            const subDirStats = await processDirectory(entryPath);
            Object.keys(subDirStats).forEach(key => {
                stats[key] += subDirStats[key];
            });
        } else {
            // 处理文件
            const fileStats = await processFile(entryPath, outputPath);
            if (fileStats) {
                stats.totalFiles++;
                if (fileStats.processed) {
                    stats.processedFiles++;
                    stats.originalSize += fileStats.originalSize;
                    stats.processedSize += fileStats.processedSize;
                }
            }
        }
    }

    return stats;
}

// 格式化字节大小
function formatBytes(bytes, decimals = 2) {
    if (bytes === 0) return '0 Bytes';

    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];

    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

// 主函数
async function main() {
    try {
        // 检查输入目录是否存在
        if (!await fs.promises.stat(INPUT_DIR).catch(() => null)) {
            console.error(`错误: 输入目录 "${INPUT_DIR}" 不存在`);
            process.exit(1);
        }

        // 清空或创建输出目录
        if (await fs.promises.stat(OUTPUT_DIR).catch(() => null)) {
            await fs.promises.rm(OUTPUT_DIR, {
                recursive: true
            });
        }
        await fs.promises.mkdir(OUTPUT_DIR, {
            recursive: true
        });

        console.log(`开始处理目录: ${INPUT_DIR}`);
        console.log(`输出目录: ${OUTPUT_DIR}`);
        console.log('--------------------------------');

        const startTime = Date.now();
        const stats = await processDirectory(INPUT_DIR);
        const endTime = Date.now();

        console.log('--------------------------------');
        console.log('处理完成!');
        console.log(`总文件数: ${stats.totalFiles}`);
        console.log(`已处理文件数: ${stats.processedFiles}`);

        if (stats.processedFiles > 0) {
            console.log(`原始总大小: ${formatBytes(stats.originalSize)}`);
            console.log(`处理后总大小: ${formatBytes(stats.processedSize)}`);

            const reduction = ((stats.originalSize - stats.processedSize) / stats.originalSize * 100).toFixed(2);
            console.log(`总体积减少: ${reduction}%`);
        }

        console.log(`耗时: ${((endTime - startTime) / 1000).toFixed(2)} 秒`);

    } catch (error) {
        console.error('执行过程中出错:', error);
        process.exit(1);
    }
}

main();